package tenant

import (
	"context"
	"strconv"

	v1 "gitee.com/liangziyisheng/caas-platform-mc/api/tenant/v1"
	"gitee.com/liangziyisheng/caas-platform-mc/internal/consts"
	"gitee.com/liangziyisheng/caas-platform-mc/internal/dao"
	"gitee.com/liangziyisheng/caas-platform-mc/internal/model/do"
	"gitee.com/liangziyisheng/caas-platform-mc/internal/model/entity"
	"gitee.com/liangziyisheng/caas-platform-mc/internal/service"
	"gitee.com/liangziyisheng/caas-platform-mc/utility/authorization"

	"github.com/gogf/gf/v2/errors/gcode"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/glog"
)

type sTenant struct{}

var l = g.Log("logger for tenant")

func New() *sTenant {
	return &sTenant{}
}

func init() {
	l.SetFlags(glog.F_FILE_LONG)
	service.RegisterTenant(New())
}

func (s *sTenant) List(ctx context.Context, in *v1.ListInput) (out *v1.ListOutput, err error) {

	cls := dao.Tenant.Columns()

	tmd := dao.Tenant.Ctx(ctx)

	if in.Status != nil {
		tmd = tmd.Where(cls.Status, *in.Status)
	}
	if in.Code != "" {
		tmd = tmd.Where(cls.Code, in.Code)
	}
	if in.Name != "" {
		tmd = tmd.WhereLike(cls.Name, "%"+in.Name+"%")
	}
	//tenants := make([]v1.Tenant, 0, 100)
	out = &v1.ListOutput{}
	err = tmd.Limit((in.Page-1)*in.Size, in.Size).ScanAndCount(&out.Data, &out.Total, true)
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.ListTenantFailed, in))
	}
	//out = &v1.ListOutput{Data: tenants, Total: total}
	return out, nil
}
func (s *sTenant) Info(ctx context.Context, in *v1.InfoInput) (out *v1.InfoOutput, err error) {
	return nil, nil
}

func (s *sTenant) Add(ctx context.Context, in *v1.AddInput) (out *v1.AddOutput, err error) {
	tx, err := g.DB().Begin(ctx)
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.AddTenantFailed, in.Tenant))
	}
	tid, err := tx.Model(dao.Tenant.Table()).InsertAndGetId(in.Tenant)
	if err != nil {
		l.Error(ctx, err)
		tx.Rollback()
		return nil, gerror.NewCode(gcode.WithCode(consts.AddTenantFailed, in.Tenant))
	}
	out = &v1.AddOutput{Id: tid}
	// 生成顶级组织
	org := do.Organization{
		Code:       in.Tenant.Code,
		Name:       in.Tenant.Name,
		Type:       consts.OrgTypes[0].Value,
		TenantCode: in.Tenant.Code,
	}
	_, err = tx.Model(dao.Organization.Table()).InsertAndGetId(org)
	if err != nil {
		l.Error(ctx, err)
		tx.Rollback()
		return nil, gerror.NewCode(gcode.WithCode(consts.AddTenantFailed, in.Tenant))
	}

	// 如果创建租户成功了,则顺便生成租户管理员
	user := do.User{}
	user.TenantCode = in.Tenant.Code
	user.Fullname = in.Tenant.Name.(string) + "-管理员"
	user.Username = in.Tenant.Code.(string) + "_admin"
	user.Password = consts.TenantAdminDefaultPass
	user.Type = consts.UserTypes[0].Value
	user.Status = consts.UserStatus[1].Value

	user.TenantAdmin = true
	uid, err := tx.Model(dao.User.Table()).InsertAndGetId(user)
	if err != nil {
		l.Error(ctx, err)
		tx.Rollback()
		return nil, gerror.NewCode(gcode.WithCode(consts.AddTenantFailed, in.Tenant))
	}
	// 创建租户管理员后赋予租户管理员权限
	e := authorization.NewEnforcer()
	_, err = e.AddGroupingPolicy(strconv.FormatInt(uid, 10), consts.TenantAdminDefaultRole)
	if err != nil {
		l.Error(ctx, err)
		tx.Rollback()
		return nil, gerror.NewCode(gcode.WithCode(consts.AddTenantFailed, in.Tenant))
	}
	tx.Commit()
	return out, nil
}

func (s *sTenant) Delete(ctx context.Context, in *v1.DeleteInput) (out *v1.DeleteOutput, err error) {
	// 查询租户信息
	tenant := entity.Tenant{}
	err = dao.Tenant.Ctx(ctx).Where(dao.Tenant.Columns().Id, in.Id).Scan(&tenant)
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.QueryTenantFailed, in.Id))
	}
	if tenant.Id == 0 {
		l.Error(ctx, "tenant not exist", in.Id)
		return nil, gerror.NewCode(gcode.WithCode(consts.TenantNotExist, in.Id))
	}

	// 查询是否存在用户在该租户之下
	ucls := dao.User.Columns()
	rows, err := dao.User.Ctx(ctx).Where(ucls.TenantCode, tenant.Code).WhereNull(ucls.TenantAdmin).Count()
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.QueryUserFailed, tenant.Code))
	}
	if rows > 0 {
		l.Error(ctx, "tenant has user", in.Id)
		return nil, gerror.NewCode(consts.TenantIncludeUser)
	}
	// 开始删除
	tx, err := g.DB().Begin(ctx)
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.DeleteTenantFailed, in.Id))
	}
	// 先删除用户
	_, err = tx.Model(dao.User.Table()).Where(ucls.TenantCode, tenant.Code).Where(ucls.TenantAdmin, true).Delete()
	if err != nil {
		l.Error(ctx, err)
		tx.Rollback()
		return nil, gerror.NewCode(gcode.WithCode(consts.DeleteTenantFailed, in.Id))
	}
	// 再删除租户
	rs, err := tx.Model(dao.Tenant.Table()).Where(dao.Tenant.Columns().Id, in.Id).Delete()
	if err != nil {
		l.Error(ctx, err)
		tx.Rollback()
		return nil, gerror.NewCode(gcode.WithCode(consts.DeleteTenantFailed, in.Id))
	}
	out = &v1.DeleteOutput{}
	if out.Rows, err = rs.RowsAffected(); err != nil {
		l.Error(ctx, err)
		tx.Rollback()
		return nil, gerror.NewCode(gcode.WithCode(consts.DeleteTenantFailed, in.Id))
	}
	tx.Commit()
	if out.Rows == 0 {
		return nil, gerror.NewCode(gcode.WithCode(consts.DeleteFailed, in.Id))
	}
	return out, nil
}

func (s *sTenant) Update(ctx context.Context, in *v1.UpdateInput) (out *v1.UpdateOutput, err error) {

	// 更新数据
	out = &v1.UpdateOutput{}
	out.Rows, err = dao.Tenant.Ctx(ctx).Data(in.Tenant).Where(dao.Tenant.Columns().Id, in.Id).UpdateAndGetAffected()
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.UpdateFailed, in.Tenant))
	}
	if out.Rows == 0 {
		return nil, gerror.NewCode(gcode.WithCode(consts.UpdateFailed, in.Id))
	}
	return out, nil
}
